/*******************************************************************************
blogger-cli Консольные инструменты по работе с blogger.com
(с) Камнев Георгий Павлович 2011 GPLv2

Данная программа является свободным программным обеспечением. Вы вправе
распространять ее и/или модифицировать в соответствии с условиями версии 2
либо по вашему выбору с условиями более поздней версии
Стандартной Общественной Лицензии GNU, опубликованной Free Software Foundation.

Мы распространяем данную программу в надежде на то, что она будет вам полезной,
однако НЕ ПРЕДОСТАВЛЯЕМ НА НЕЕ НИКАКИХ ГАРАНТИЙ,
в том числе ГАРАНТИИ ТОВАРНОГО СОСТОЯНИЯ ПРИ ПРОДАЖЕ
и ПРИГОДНОСТИ ДЛЯ ИСПОЛЬЗОВАНИЯ В КОНКРЕТНЫХ ЦЕЛЯХ.
Для получения более подробной информации ознакомьтесь
со Стандартной Общественной Лицензией GNU.

Вместе с данной программой вы должны были получить экземпляр
Стандартной Общественной Лицензии GNU.
Если вы его не получили, сообщите об этом в Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*******************************************************************************/

package tv.cofe.blogger;

import com.google.gdata.data.Category;
import com.google.gdata.data.Content;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.Entry;
import com.google.gdata.data.Link;
import com.google.gdata.data.TextConstruct.RssFormat;
import com.google.gdata.data.TextContent;
import com.google.gdata.util.common.xml.XmlWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXSource;
import org.gocha.collection.Convertor;
import org.gocha.files.FileUtil;
import org.gocha.text.IndentStackWriter;
import org.gocha.text.TextUtil;
import org.gocha.xml.XMLUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * Функции по работе с постом
 * @author gocha
 */
public class PostHelper
{
    /**
     * Возвращает плоскую карту свойств поста.<br/>
     * Карта содержит слежующие ключи: 
     * <b>
     * id, title, published, edited, updated, canEdit, draft, categories, self
     * </b>
     * @param e Пост
     * @return Карта свойств
     */
    public static Map<String,String> getFlatProperties(Entry e,Convertor<Entry,String> idConvertor,Convertor<DateTime,String> timeConvertor){
        if (e == null) {            
            throw new IllegalArgumentException("e==null");
        }
        
        HashMap<String, String> v = new HashMap<String, String>();
        StringBuilder sb = new StringBuilder();
        
        v.put("id", idConvertor!=null ? idConvertor.convert(e) : e.getId() );
        v.put("title",
                e.getTitle() != null
                ? e.getTitle().getPlainText() : "null");

        v.put("published",
                e.getPublished() != null ? 
                (timeConvertor!=null ? timeConvertor.convert(e.getPublished()) : e.getPublished().toStringRfc822())
                : "null");
        v.put("edited",
                e.getEdited() != null ? 
                (timeConvertor!=null ? timeConvertor.convert(e.getEdited()) : e.getEdited().toStringRfc822())
                : "null");
        v.put("updated",
                e.getUpdated() != null ? 
                (timeConvertor!=null ? timeConvertor.convert(e.getUpdated()) : e.getUpdated().toStringRfc822())
                : "null");

        v.put("canEdit", e.getCanEdit() ? "true" : "false");
        v.put("draft", e.isDraft() ? "true" : "false");

        sb.setLength(0);
        int i = -1;
        for (Category cat : e.getCategories()) {
            i++;
            if (i > 0)
                sb.append(", ");
            sb.append(cat.getTerm());
        }
        v.put("categories", sb.toString());

        Link self = e.getSelfLink();
        v.put("self",self==null ? "null" : self.getHref());
        
        return v;
    }

    protected static String hookRssBody(TextContent txtConent) throws IOException, SAXException{
        if (txtConent== null) {            
            throw new IllegalArgumentException("txtConent==null");
        }
        if( txtConent.getContent().isEmpty() )return "";
        
        StringWriter strw = new StringWriter();
        XmlWriter xmlWriter = new XmlWriter(strw);
        txtConent.getContent().generateRss(xmlWriter, "body", RssFormat.FULL_HTML);
        xmlWriter.flush();
        String xml = strw.toString();
        
        Document xdoc = XMLUtil.parseXML(xml);
        Element e = xdoc.getDocumentElement();
        return e.getTextContent();
    }
    
    protected static String getHtmlBodyOf(TextContent txtContent)
            throws IOException, SAXException
    {
        String body = hookRssBody((TextContent)txtContent);
        if( body==null ) throw new Error(CLI.messages().cantHookHtml());

        body = body.replace("&lt;", "<");
        body = body.replace("&amp;", "&");
        body = body.replace("&gt;", ">");
        body = body.replace("&quot;", "\"");
        return body;
    }
    
    /**
     * Парсинг html (возможно не валидного) в XML Document
     * @param html html разметка
     * @return XML Document
     * @throws IOException Ошибка XML формата
     * @throws SAXException Ошибка XML формата
     */
    public static org.w3c.dom.Node parseHtml(String html) {
//        return parseHtml_TagSoupImpl(html);
        return parseHtml_htmlcleanerImpl(html);
    }
    
    private static org.w3c.dom.Node parseHtml_htmlcleanerImpl(String html) {
        try{
            //---------------------
            // HtmlCleaner
            org.htmlcleaner.CleanerProperties clProps = new org.htmlcleaner.CleanerProperties();
            clProps.setOmitComments(false);
            clProps.setOmitDeprecatedTags(false);
            clProps.setOmitUnknownTags(false);
    //        clProps.setOmitDoctypeDeclaration(false);
    //        clProps.setOmitXmlDeclaration(false);
            
            clProps.setUseEmptyElementTags(true);
            clProps.setTreatUnknownTagsAsContent(false);
            clProps.setTreatDeprecatedTagsAsContent(false);
            
            org.htmlcleaner.HtmlCleaner cleaner = new org.htmlcleaner.HtmlCleaner(clProps);
            org.htmlcleaner.TagNode tagNode = cleaner.clean(html);
            org.htmlcleaner.Serializer xmlSer = new org.htmlcleaner.SimpleHtmlSerializer(clProps);
            
            String xml = xmlSer.getAsString(tagNode);
            return XMLUtil.parseXML(xml).getDocumentElement();
        }catch (Throwable ex) {
            throw new Error(ex);
        }
    }
    
    private static org.w3c.dom.Node parseHtml_TagSoupImpl(String html) {
        try {
            if (html== null) {            
                throw new IllegalArgumentException("html==null");
            }
            
            org.ccil.cowan.tagsoup.Parser parser = new org.ccil.cowan.tagsoup.Parser();
            org.xml.sax.XMLReader reader = parser;
            
            reader.setFeature(org.ccil.cowan.tagsoup.Parser.namespacesFeature, false);
            reader.setFeature(org.ccil.cowan.tagsoup.Parser.namespacePrefixesFeature, false);
            
            StringReader strReader = new StringReader(html);
            SAXSource source = new SAXSource(reader, new InputSource(strReader));
            
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            DOMResult result = new DOMResult();
            
            transformer.transform(source, result);
            Node n = result.getNode();
            return n;
        } catch (Throwable ex) {
            throw new Error(ex);
        }
    }
    
    /**
     * Экспортирует пост в файл html.
     * @param target Файл
     * @param entry Пост
     * @param htmlTmpl Шаблон файла:<br/>
     * <pre>
     * &lt;!DOCTYPE html>
     * &lt;html>
     * &lt;head>
     * &lt;title><b>{title}</b>&lt;/title>
     * &lt;meta http-equiv="content-type" content="text/html; charset=<b>{charset}</b>"/>
     * <b>{htmlMeta}</b>
     * &lt;/head>
     * &lt;body>
     * <b>{htmlBody}</b>
     * &lt;/body>
     * &lt;/html>
     * </pre>
     * + Переменные из функции getFlatProperties
     * @param metaTmpl Шаблон свойств:<br/>
     * <pre>
     * &lt;meta name="<b>{key}</b>" content="<b>{value}</b>" />
     * </pre>
     * @see #getFlatProperties(com.google.gdata.data.Entry, org.gocha.collection.Convertor) 
     */
    public static void export(File target,Entry entry,String htmlTmpl,String metaTmpl,Convertor<DateTime,String> timeConvertor) {
        if (target== null) throw new IllegalArgumentException("target==null");
        if (entry== null) throw new IllegalArgumentException("entry==null");
        if (htmlTmpl== null) throw new IllegalArgumentException("htmlTmpl==null");
        if (metaTmpl== null) throw new IllegalArgumentException("metaTmpl==null");
        
        Content content = entry.getContent();
        if( !(content instanceof TextContent) )
            throw new Error( 
                    CLI.messages().contentNotInstanceOfText() );
        try{
            String htmlBody = getHtmlBodyOf((TextContent)content);
            Map<String,String> props = getFlatProperties(entry, CLI.postIDConvertor, timeConvertor);
            
            Charset cs = FileUtil.UTF8();
            
            FileOutputStream fout = new FileOutputStream(target);
            OutputStreamWriter strW = new OutputStreamWriter(fout, cs);
            IndentStackWriter writer = new IndentStackWriter(strW);
            
            HashMap<String,String> t = new HashMap<String, String>();
            writer.push();
            for(Map.Entry<String,String> kv : props.entrySet()){
                t.put( "key", TextUtil.htmlEncode(kv.getKey()) );
                t.put( "value", TextUtil.htmlEncode(kv.getValue()) );
                writer.println(TextUtil.template(metaTmpl, t));
            }
            String htmlMeta = writer.pop();
            
            t.putAll(props);
            t.put("htmlMeta", htmlMeta);
            t.put("htmlBody", htmlBody);
            t.put("charset", cs.name());
            writer.println(TextUtil.template(htmlTmpl, t));
            
            writer.flush();
            strW.flush();
            fout.flush();
            
            fout.close();
        }catch(Throwable e){
            throw new Error(e.getMessage(), e);
        }
    }
}
